package com.qianniu.napi.admin.web;

import akka.actor.ActorRef;
import akka.actor.Props;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.google.common.base.Strings;
import com.jpay.ext.kit.IpKit;
import com.jpay.ext.kit.StrKit;
import com.qianniu.napi.admin.entity.*;
import com.qianniu.napi.admin.entity.wxbean.WeChat;
import com.qianniu.napi.admin.service.*;
import com.qianniu.napi.admin.service.akka.AkkaServer;
import com.qianniu.napi.admin.service.akka.UserActor;
import com.qianniu.napi.common.config.Constants;
import com.qianniu.napi.common.util.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by chenxuebin on 2017/8/9.
 */
@Controller
public class WeixinController {

    @Autowired
    private ICouponService couponService;
    @Autowired
    private ICouponTplService couponTplService;

    @Autowired
    private IUserService userService;
    @Autowired
    private IRedisService redisService;
    @Autowired
    private RedissonClient redis;

//    @Autowired
//    private CacheManager cacheManager;

    @Autowired
    private IMerchantService merchantService;

    @Autowired
    private IMerchantAppService merchantAppService;

    @Autowired
    private IMerchantConfigService merchantConfigService;
    @Autowired
    private IWeixinService weixinService;

    private static final Logger logger = LoggerFactory.getLogger(WeixinController.class);

    @RequestMapping("/test")
    @ResponseBody
    public JsonDO test() {

        JsonDO json = new JsonDO();
        json.setData("success");
        return json;
    }

    @RequestMapping(value = "/api/user/login", method = RequestMethod.POST)
    @ResponseBody
    public Map decodeUserinfo(HttpServletRequest req,
                              @RequestParam("encryptedData") String encryptedData,
                              @RequestParam("iv") String iv,
                              @RequestParam("code") String code,
                              @RequestParam(value = "g",required = false) String g,
                              @RequestParam(value = "s",required = false) String s,
                              @RequestParam(value = "ud",required = false) String ud,
                              @RequestParam(value = "ori", required = false) String ori
    ) throws Exception {
        //初始化配置文件
        Map map = new HashMap();
        logger.debug("======g="+g);
        logger.debug("======s="+s);
        logger.debug("======ud="+ud);

        //登录凭证不能为空
        if (code == null || code.length() == 0) {
            map.put("status", 1);
            map.put("msg", "code 不能为空");
            return map;
        }
        Long mid = ObjectUtil.getMid();
        String appid = ObjectUtil.getAppid();
        if (mid == null || mid <= 0) {
            map.put("code", 1);
            map.put("msg", "paramter error");
            return map;
        }

        // add redis cache
        MerchantApp mApp = redisService.getMApp();

        if (mApp == null) {
            map.put("code", 2);
            map.put("msg", "未找到商家应用配置");
            return map;
        }

        if (Strings.isNullOrEmpty(mApp.getAppid())) {
            map.put("code", 2);
            map.put("msg", "商家应用还未初始化配置");
            return map;
        }

        // mApp.getAppid() 为非(多门店多分店）商家默认的appid 只有一个
        // ObjectUtil.getAppid() 为多门店多分点商家对应其中某一个的appid
        appid = mApp.getAppid();
        Long mpid = mApp.getId();
        String secret = mApp.getAppsecret();

        //授权（必填）
        String grant_type = "authorization_code";
        String rskey = Constants.REDIS_SESSION_KEY+code;
        String sessionKey = redisService.get(rskey);

        if(Strings.isNullOrEmpty(sessionKey)) {
            //////////////// 1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid ////////////////
            //请求参数
            String params = "appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=" + grant_type;
            logger.debug("========params=" + params);
            //发送请求
            String sr = HttpUtil.sendGet(Constants.WX_URL_JSCODE2SESSION, params);
            //解析相应内容（转换成json对象）
            JSONObject json = JSONObject.parseObject(sr);
            logger.debug(">>>json=" + json.toJSONString());
            //获取会话密钥（session_key）
            sessionKey = json.get("session_key").toString();
            redisService.set(rskey,sessionKey,5, TimeUnit.MINUTES);
        }
        //用户的唯一标识（openid）
//        String openid = (String) json.get("openid");
        logger.debug(">>>session_key=" + sessionKey + " ori=" + ori);
        map.clear();

        //////////////// 2、对encryptedData加密数据进行AES解密 ////////////////
        try {
            String result = AesUtil.decrypt(encryptedData, sessionKey, iv);
            if (null != result && result.length() > 0) {
                map.put("code", 0);
                map.put("msg", "ok");

                JSONObject userInfoJSON = JSONObject.parseObject(result);
                Map userInfo = new HashMap();

                String ip = ObjectUtil.getIP();
//                String location = ObjectUtil.getLocation();
                logger.debug("======ip="+ip);

                userInfo.put("openId", userInfoJSON.get("openId"));
                userInfo.put("nickName", userInfoJSON.get("nickName"));
                userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl"));
                userInfo.put("gender", userInfoJSON.get("gender"));
                userInfo.put("city", userInfoJSON.get("city"));
                userInfo.put("province", userInfoJSON.get("province"));
                userInfo.put("country", userInfoJSON.get("country"));
                userInfo.put("unionId", userInfoJSON.get("unionId"));
                userInfo.put("appid", appid);
                userInfo.put("appType", ObjectUtil.getAppType());
                userInfo.put("ip", ip);

                if(!Strings.isNullOrEmpty(g)) userInfo.put("g", g);
                if(!Strings.isNullOrEmpty(ud)) userInfo.put("ud", ud);
                userInfo.put("mid", mApp.getMid());
                userInfo.put("mpid", mApp.getId());
                if (!Strings.isNullOrEmpty(ori)) userInfo.put("ori", ori);

                logger.debug(">>>userInfo=" + userInfo);
                User user = updateUserinfo(userInfo);

                logger.debug(">>>ObjectUtil.getMpid()=" + ObjectUtil.getMpid());
                logger.debug(">>>user.isIsnew()=" + user.getNewuser());
                if (ObjectUtil.getMpid() == 2 && user.getNewuser()) {
                    logger.debug(">>>ready to tell akka msg");
                    // 名片,异步创建用户统计信息
                    AkkaServer.system.actorOf(Props.create(UserActor.class))
                            .tell(Constants.AKKA_MSG_CUSERSTAT + "@" + user.getId(), ActorRef.noSender());
                }

//                userInfo.remove("openId");
                userInfo.remove("unionId");
                userInfo.put("uid", user.getId());

                String key = ObjectUtil.getKey(user,mpid);
                String token = AesUtil.encrypt(key, Constants.ENCODE_KEY);
                EhcacheUtil.getInstance().put(Constants.EHCACHE_KEY, token, key);

                //处理分销
                MerchantConfig mconf = redisService.getMconfig();
                if(mconf.getIsFenxiao()==1){
                    logger.debug("======start to record fenxiao");
                    if(!Strings.isNullOrEmpty(ori))AkkaServer.system.actorOf(Props.create(UserActor.class))
                            .tell(Constants.AKKA_MSG_FENXIAO_ORI + "@" +ori+ "@" + user.getId(), ActorRef.noSender());
                    else if(!Strings.isNullOrEmpty(s))AkkaServer.system.actorOf(Props.create(UserActor.class))
                            .tell(Constants.AKKA_MSG_FENXIAO_SCENE+ "@" +s+"@"+ user.getId(), ActorRef.noSender());
                }

//                userInfo.put("hasmp", user.getHasmp());
                userInfo.put("token", token);
//                map.put("info", AesUtil.encrypt(JSON.toJSONString(userInfo),mApp.getAeskey(),mApp.getAesiv()));
                map.put("guid", user.getGuid());
                map.put("ismch", user.getIsMch());
                map.put("token", token);

                return map;
            }
        } catch (Exception e) {
            logger.error("==========encryptedData and process user data error", e);
        }

        map.put("status", 0);
        map.put("msg", "解密失败");
        return map;
    }

    @RequestMapping(value = "/api/user/lanuch")
    @ResponseBody
    public Map lanuch(@RequestParam("t") String t) throws Exception {
        //初始化配置文件
        Map map = new HashMap<String, String>();
        logger.info("========t=" + t);
        User user = redisService.getUser();
        if (user != null && user.getSid() != null && user.getSid() > 0
                && ObjectUtil.getSid() == null) {
            EhcacheUtil.getInstance().remove(Constants.EHCACHE_KEY, t);
            MerchantApp mApp = redisService.getMApp();
            String key = mApp.getMid() + "__" + mApp.getId() + "__" + user.getId().toString() + "__" + ObjectUtil.randomStrings(8, 1).get(0) + "__" + mApp.getAppid() + "__" + mApp.getAppType() + "__" + user.getSid();
            String token = AesUtil.encrypt(key, Constants.ENCODE_KEY);
            EhcacheUtil.getInstance().put(Constants.EHCACHE_KEY, token, key);
            map.put("token", token);
        }

        return map;
    }

    @RequestMapping(value = "/api/user/jiemiwx", method = RequestMethod.POST)
    @ResponseBody
    public Jinfo jiemiwx(HttpServletRequest req,
                         @RequestParam("encryptedData") String encryptedData,
                         @RequestParam("iv") String iv,
                         @RequestParam(value = "act", required = false) Integer act,
                         @RequestParam("code") String code,
                         @RequestParam("t") String t
    ) throws Exception {
        //初始化配置文件
        Jinfo info = new Jinfo();
        logger.info("========params");

        //登录凭证不能为空
        if (code == null || code.length() == 0) {
            info.setCode(1);
            info.setMsg("paramter error");
            return info;
        }
        Long mid = ObjectUtil.getMid();
        String rkey = Constants.REDIS_APP + mid + "_" + ObjectUtil.getAppid();
        logger.info("===============rkey=" + rkey);

        MerchantApp mApp = redisService.getMApp();

        Long mpid = mApp.getId();
        String appid = ObjectUtil.getAppid();
        String secret = mApp.getAppsecret();

        logger.info("===============secret=" + secret);

        //授权（必填）
        String grant_type = "authorization_code";
        String rskey = Constants.REDIS_SESSION_KEY+code;
        String sessionKey = redisService.get(rskey);

        if(Strings.isNullOrEmpty(sessionKey)) {
            //////////////// 1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid ////////////////
            //请求参数
            String params = "appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=" + grant_type;
            logger.info("===============params=" + params);
            //发送请求
            String sr = HttpUtil.sendGet(Constants.WX_URL_JSCODE2SESSION, params);
            logger.debug(">>>sr=" + sr);
            //解析相应内容（转换成json对象）
            JSONObject json = JSONObject.parseObject(sr);
            //获取会话密钥（session_key）
            sessionKey = json.get("session_key").toString();
            redisService.set(rskey,sessionKey,5, TimeUnit.MINUTES);
        }
        //用户的唯一标识（openid）
//        String openid = (String) json.get("openid");
        logger.info("===============session_key=" + sessionKey);

        //////////////// 2、对encryptedData加密数据进行AES解密 ////////////////
        try {
            String result = AesUtil.decrypt(encryptedData, sessionKey, iv);
            if (null != result && result.length() > 0) {
                logger.info("===============result=" + result);
                JSONObject jo = JSONObject.parseObject(result);

                User user = new User();
                logger.info("=============================================ObjectUtil.getUid()=" + ObjectUtil.getUid());
                if (jo.containsKey("phoneNumber")&&ObjectUtil.getUid()!=null) {
                    user.setId(ObjectUtil.getUid());
                    user.setPhone(jo.getString("phoneNumber"));
                    user.insertOrUpdate();
                    // 通知积分奖励
                    AkkaServer.system.actorOf(Props.create(UserActor.class))
                            .tell(Constants.AKKA_MSG_BIND_PHONE+ "@" + user.getId(), ActorRef.noSender());
                    // 更新用户缓存信息
                    redisService.del(Constants.REDIS_APP_USER + ObjectUtil.getMid() + "_" + ObjectUtil.getUid());
                }

                // 一个老板可能多个商家##待改进
                String adminmpid = Constants.MADMIN_APP_MAP.get(ObjectUtil.getAppid());
                user = userService.selectOne(new EntityWrapper<User>().eq("phone", jo.getString("phoneNumber")).eq("is_platform", 1).ne("apptype", 9));
                logger.info("===============muser=" + JSON.toJSONString(user));

                if (user != null && jo.containsKey("phoneNumber") && act != null && act == 1) {
                    String key = ObjectUtil.getKey(user,mpid);
                    String token = AesUtil.encrypt(key, Constants.ENCODE_KEY);
                    EhcacheUtil.getInstance().remove(Constants.EHCACHE_KEY, t);
                    EhcacheUtil.getInstance().put(Constants.EHCACHE_KEY, token, key);
                    logger.info("========jiemiwx token=" + token);
                    logger.info("========jiemiwx key=" + key);
                    info.setToken(token);
                }

                if (user == null) {
                    info.setCode(4);
                }

                info.setInfo(jo);
                return info;
            } else {
                info.setCode(1);
                info.setMsg("parse and decrypt error");
            }
        } catch (Exception e) {
            logger.error("======jiemiwx error", e);
        }

        return info;
    }

    private User updateUserinfo(Map uMap) {

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("openid", uMap.get("openId"));

        logger.debug(">>>updateUserinfo uMap=" + uMap);

        List<User> list = userService.selectByMap(map);
        User user = list == null || list.size() == 0 ? null : list.get(0);
        logger.debug(">>>user openid=" + user);
        if (user == null) {
            logger.debug(">>>user openid 111");
            user = new User();
            user.setOpenid(uMap.get("openId").toString());
            user.setNickName(uMap.get("nickName").toString());
            user.setAvatarPic(uMap.get("avatarUrl").toString());
            user.setGender(uMap.get("gender").toString());

            user.setCity(uMap.get("city").toString());
            user.setProvince(uMap.get("province").toString());
            user.setCountry(uMap.get("country").toString());
            user.setMid(Long.valueOf(uMap.get("mid").toString()));
            user.setMpid(Long.valueOf(uMap.get("mpid").toString()));
            user.setApptype(Integer.valueOf(uMap.get("appType").toString()));

            user.setIsMember(-1);
            user.setHasmp(0);
            user.setState(1);
            user.setIsMch(-1);
            if(uMap.containsKey("g"))
                user.setGuid(uMap.get("g").toString());
            else
                user.setGuid(StringUtil.getGUID());

            if(uMap.containsKey("ud"))
                user.setUd(uMap.get("ud").toString());

            if(uMap.containsKey("ip"))
                user.setIp(uMap.get("ip").toString());

            if(uMap.containsKey("location"))
                user.setLocation(uMap.get("location").toString());

//            if (uMap.containsKey("ori")) {
//                Long ori = Long.valueOf(uMap.get("ori").toString());
//                user.setPid(ori);
//            }
//            user.setSp(uMap.get("sp").toString());
            logger.debug(">>>user openid 222");
            user.setCreatetime(new Date());
            user.setMid(ObjectUtil.getMid());

            if (uMap.containsKey("unionId") && uMap.get("unionId") != null) {
                user.setUnionid(uMap.get("unionId").toString());
            }
            user.insert();
            user.setNewuser(true);

            // 记录粉丝和渠道
            if (uMap.containsKey("ori") && uMap.get("ori") != null
                    && uMap.get("ori").toString().length() > 0
                    && !uMap.get("ori").equals("undefined")) {
                String ori = uMap.get("ori").toString();

                String[] oriarr = ori.contains("%") ? ori.split("%") : ori.split("_");//uid+page+act+ywid
//                User puser = null;
//                if(oriarr.length==1&&oriarr[0].indexOf("UID#")>0&&oriarr[0].length()>4){
//                    puser = userService.selectById(oriarr[0].substring(4));
//                }else{
//                    puser = userService.selectOne(new EntityWrapper<User>().eq("guid", oriarr[0]));
//                }
                User puser = userService.selectOne(new EntityWrapper<User>().eq("guid", oriarr[0]));
                if (puser != null && !Strings.isNullOrEmpty(oriarr[0])) {
                    Fens fen = new Fens();
                    fen.setNickname(user.getNickName());
                    fen.setAvatarpic(user.getAvatarPic());
                    fen.setLevelType(1);// 默认记录一级粉丝、二级三级由定时任务根据商家配置来实现
                    fen.setMid(ObjectUtil.getMid());
                    fen.setMpid(ObjectUtil.getMpid());
                    fen.setUid(user.getId());
                    fen.setPuid(puser.getId());

                    fen.setPage(oriarr[1].contains("/") ? oriarr[1] : oriarr[1] + "/" + oriarr[1]);
                    fen.setAct(oriarr[2]);
                    fen.setYwid(oriarr[3]);

                    fen.setStatus(1);// 默认粉丝状态，后续开通会员再升级
                    fen.setState(1);// 默认有效，如果通过别人购买链接，那算别人的了
                    fen.setOriginType(3);// 默认粉丝状态，后续开通会员@1：海报连接；2：激活码；3：转发
                    // ****** 待完善 ******
                    //fen.setBonuses(100);// 根据系统制定规则发放奖励
                    fen.insert();
                } else {
//                    user.setOriginType();
                }
            }

            logger.debug(">>>user openid 333");
            // 插入默认账户信息
            MerchantConfig mc = merchantConfigService.selectList(new EntityWrapper<MerchantConfig>().eq("appid", uMap.get("appid").toString())).get(0);
            BigDecimal zero = new BigDecimal(0);
            if (mc.getIsMember() == 1) {
                Account act = new Account();
                act.setUid(user.getId());
                act.setMid(user.getMid());
                act.setMpid(user.getMpid());
                act.setCreatetime(user.getCreatetime());
                act.setUnionid(user.getUnionid());

                act.setTotal(zero);
                act.setAmount(zero);
                act.setUnuseAmount(zero);
                act.setFanli(zero);
                act.setFanliing(zero);
                act.setFreeze(zero);
                act.setChargeAmount(zero);
                act.setMamount(zero);
                act.setWithdraw(zero);
                act.insert();
            }
            if (mc.getIsJifen() == 1) {
                Jifen jifen = new Jifen();
                jifen.setState(1);
                jifen.setUid(user.getId());
                jifen.setMid(user.getMid());
                jifen.setMpid(user.getMpid());
                jifen.setCreatetime(user.getCreatetime());
                jifen.setTotal(new BigDecimal(0));
                jifen.insert();
            }
        } else {
            // 检查用户字段是否用变更，主要是avatarUrl+nickName检查
            if(user.getGuid()!=null&&uMap.containsKey("guid")&&user.getGuid().equals(uMap.get("guid").toString())){
                uMap.put("guid",user.getGuid());
                uMap.put("updateGuid",true);
            }
        }
        if (uMap.containsKey("ori")) process(uMap.get("ori").toString(), user);

        return user;
    }

    private void process(String origin, User user) {
        if (Strings.isNullOrEmpty(origin) || user == null) return;
        if (origin.contains("btn[lingquan]")) {
            String[] oriarr = origin.split("%");//uid+page+act+ywid
            Coupon coupon = new Coupon();
            coupon.setTplid(Long.valueOf(oriarr[3]));
            CouponTpl ctpl = couponTplService.selectById(coupon.getTplid());
            // 不能重复领取
            if (ctpl.getIsRepeat() != 1) {
                Coupon one = couponService.selectOne(new EntityWrapper<Coupon>().eq("tplid", oriarr[3]).eq("uid", user.getId()));
                if (one != null) {
                    return;
                }
            }
            coupon.setName(ctpl.getName());
            coupon.setType(ctpl.getType());
            coupon.setCno(StringUtil.getQuanCode(ObjectUtil.getMid()));

            coupon.setIsForever(ctpl.getIsForever());
            coupon.setStarttime(DateUtil.getToday());
            coupon.setEndtime(ctpl.getIsForever() == 1 ? null : ctpl.getEndtime());
            coupon.setCost(ctpl.getCost());
            coupon.setUsemin(ctpl.getUsemin());

            coupon.setMid(user.getMid());
            coupon.setUid(user.getId());
            coupon.setState(1);
            coupon.setIsUsed(0);
            coupon.setIsOutdate(0);
            coupon.setIsUnused(1);
            coupon.setOriginType(1);//领券来源@1：领取；2：分享；3：赠送&
            coupon.setCreatetime(new Date());//领券来源@1：领取；2：分享；3：赠送&

            coupon.insert();
            ctpl.setUse(ctpl.getUse() + 1);
            ctpl.insertOrUpdate();
        }
    }

    // =========== below for gongzhonghao ==========
    @RequestMapping(value = "/wx/{mpid}/api", method = RequestMethod.GET, produces = "text/plain;charset=utf-8")
    @ResponseBody
    public void connect(WeChat wc, @PathVariable String mpid, HttpServletResponse rsp) {
        String signature = wc.getSignature(); // 微信加密签名
        String timestamp = wc.getTimestamp(); // 时间戳
        String nonce = wc.getNonce();// 随机数
        String echostr = wc.getEchostr();// 随机字符串
        Session s = SecurityUtils.getSubject().getSession();
        s.setAttribute("mpid", mpid);

        // 通过检验signature对请求进行校验，若校验成功则原样返回echostr，表示接入成功，否则接入失败
        if (SignUtil.checkSignature(signature, timestamp, nonce, mpid)) {
            logger.debug("========return echostr=" + echostr);
        } else {
            logger.warn("不是微信服务器发来的请求,请小心!");
            echostr="不是微信服务器发来的请求,请小心!";
        }

        try {
            BufferedOutputStream out = new BufferedOutputStream(rsp.getOutputStream());
            out.write(echostr.getBytes());
            out.flush();
            out.close();
        } catch (Exception e) {
            logger.error("======wx connect error", e);
        }
    }

    @RequestMapping(value = "/wx/{mpid}/api", method = RequestMethod.POST)
    @ResponseBody
    public String getWeiXinMessage(HttpServletRequest request, @PathVariable String mpid) throws Exception {
        //初始化配置文件
        Session s = SecurityUtils.getSubject().getSession();
        s.setAttribute("mpid", mpid);
        String respMessage = weixinService.processRequest(request, mpid);//调用CoreService类的processRequest方法接收、处理消息，并得到处理结果；

        // 响应消息
        //调用response.getWriter().write()方法将消息的处理结果返回给用户
        return respMessage;
    }

    @RequestMapping(value = "/wx/{mpid}/oauth2", method = RequestMethod.GET)
    @ResponseBody
    public String oauth2(HttpServletRequest request, @PathVariable String wxType) throws Exception {
        //初始化配置文件
        String respMessage = weixinService.processRequest(request, wxType);//调用CoreService类的processRequest方法接收、处理消息，并得到处理结果；

        // 响应消息
        //调用response.getWriter().write()方法将消息的处理结果返回给用户
        return respMessage;
    }
}
